﻿//-------------------------------------------------------------------
//	Copyright (c) 2012 Fedotov I.V.
//	All rights reserved.
//-------------------------------------------------------------------
/*
	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program. If not, see http://www.gnu.org/licenses/.

	Project SVN page 
*/

//Менеджер ресурсов (ResManager)
//  Для считывания данных resManagerу нужно 2 этапа: загрузка и разбор в объекты одного или нескольких 
//  fileContainer путем чтения как самих контейнеров (addAndParseAtContainer()) так и просто указанием имени
//  файла (addAndParseAtFile()) с контейнером и второго этапа -  установки для всех считанных объектов 
//  взаимных связей при помощи checkAndSetAllObjectsAtIDs() после считывания всех данных.
//
//  При сохранении запускается функция saveAndJoinAll(). Она выполняет функцию join() для всех обектов в ResManager
//  для того чтобы обновить данные всех fileContainer для каждого объекта. Далее выполняется последовательное сохранение
//  всех контенеров из "списка" fileContainers.
//  Так как очевидно не все объекты могут требовать сохранения, то для того чтобы объект был сохранен, следует
//  сначала добавить его контейнер в любой контейнер из списка или в список контейнеров, только тогда он сможет
//  сохраняться. Также всё содержимое контейнера, помеченного как FC_FLAG_READ_ONLY не будет сохраняться.
//
//  Считывание:
//1.Разбор содержимого контейнеров (fileContainer) в объекты в картах map.
//  В контейнере данные представлены иерархически, а в ResManager как "списки" (карты map) однотипных обектов
//  без сохранения их иерархии. При этом каждый объект списка имеет свой fileContainer (он может быть пустым
//  или добавлен к какому-то контейнеру). Таким образом одновременно достигается сохранение иерархии объектов
//  в файле (fileContainer) и плоского размещения обектов в ResManager.
//  Для того чтобы сохранить иерархию внутри самих объектов, для всех объектов применяются уникальные 
//  идентификаторы.
//2.После считывания и разбора данных в контейнерах следует запустить функцию checkAndSetAllObjectsAtIDs().
//  Она запустит аналогичную функцию для всех объектов из списков resManager и установит во всех
//  объектах указатели на все требуемые объекты внутри них. Если не провести такую процедуру, то
//  объекты будут проводить её самомтоятельно в момент вызова (возможны тормоза)
//
//  Сохранение:
//1.Запустить saveAndJoinAll();
//  Все контейнеры из списка fileContainers получат обновление своих данных из объектов (при помощи join) 
//  и не помеченные флагом FC_FLAG_READ_ONLY контейнеры у которых установлен путь для сохранения
//  будут сохранены в свои файлы.
//
//  Примечания:
//  Путь сохранения устанавливается в каждый сохраняемый контейнер самостоятельно.
//  Так как resManager задуман как один объект (в проекте не требуется еще resManager)
//  то обращение к нему должно идти через: _global.RM 
//  Создание другого resManager приведет к неправильной работе (в нём не будет работать 
//  checkAndSetAllObjectsAtIDs())


//Для добавления контейенра нового типа, можно скопировать langstring.h/cpp (там контейнер охватывает
//большинство действий)
//Далее модификации подвергнутся файлы filecontainer.h (ContainerType), resmanager.h/cpp и idlist.h/cpp
//Там по аналогии с уже готовыми функциями и переменными следует добавить функции и переменные для нового типа
//Желательно при этом придерживаться "принятого шаблона названий" для упрощения понимания кода.
//Также для автоматизации добавления контейнеров могут использоваться макросы. Для этого предусмотрены метки
//MACROS.000-MACROS.021. (Файл контейнера, аналогичный langstring.h/cpp не учитывается и добавляется вручную)

//Комментарии по контейнеру можно найти в ustrings.h/cpp


/*
  Example:

	//Save
	
	SPtr<UString> ustr1,ustr2,ustr3,ustr4,ustr5;
	SPtr<UString> ustr_langName1,ustr_langName2,ustr_langName3;
	SPtr<UString> ustr_langCaption1,ustr_langCaption2,ustr_langCaption3;
	SPtr<UString> ustr_languageCaption1,ustr_languageCaption2,ustr_languageCaption3;
	SPtr<LangPairString> langPair1,langPair2,langPair3;
	SPtr<LangPairString> languagePair1,languagePair2,languagePair3;
	SPtr<LangSelector> langSelector1;
	SPtr<LangString> langStr1;
	SPtr<FileContainer> spFCContainer1,spFCContainer2,spFCContainer3;
	SPtr<Languages> spLanguages;

	wstring fname=L"c:/tmp/1/1.txt\0";

  /*
	//Save
	_global.RM.addAndParseAtContainer(spFCContainer1);

	_global.RM.addUString(ustr1);
	_global.RM.addUString(ustr2);
	_global.RM.addUString(ustr3);
	_global.RM.addUString(ustr4);
	_global.RM.addUString(ustr5);

	_global.RM.addUString(ustr_langName1);
	_global.RM.addUString(ustr_langName2);
	_global.RM.addUString(ustr_langName3);

	_global.RM.addUString(ustr_langCaption1);
	_global.RM.addUString(ustr_langCaption2);
	_global.RM.addUString(ustr_langCaption3);

	_global.RM.addUString(ustr_languageCaption1);
	_global.RM.addUString(ustr_languageCaption2);
	_global.RM.addUString(ustr_languageCaption3);

	_global.RM.addLangPairString(langPair1);
	_global.RM.addLangPairString(langPair2);
	_global.RM.addLangPairString(langPair3);
	
	_global.RM.addLangPairString(languagePair1);
	_global.RM.addLangPairString(languagePair2);
	_global.RM.addLangPairString(languagePair3);

	_global.RM.addLangSelector(langSelector1);

	_global.RM.addLangString(langStr1);
	_global.RM.addLanguages(spLanguages);

	ustr_langName1.p()->wstr=L"en";
	ustr_langName2.p()->wstr=L"ru";
	ustr_langName3.p()->wstr=L"x";

	ustr_languageCaption1.p()->wstr=L"English";
	ustr_languageCaption2.p()->wstr=L"Русский";
	ustr_languageCaption3.p()->wstr=L"Язык древний, какой-то";

	ustr_langCaption1.p()->wstr=L"English phrase";
	ustr_langCaption2.p()->wstr=L"Русская фраза";
	ustr_langCaption3.p()->wstr=L"Непереводимая игра слов";

	langPair1.p()->setLanguage(ustr_langName1);
	langPair1.p()->setCaption(ustr_langCaption1);
	langPair2.p()->setLanguage(ustr_langName2);
	langPair2.p()->setCaption(ustr_langCaption2);
	langPair3.p()->setLanguage(ustr_langName3);
	langPair3.p()->setCaption(ustr_langCaption3);

	languagePair1.p()->setLanguage(ustr_langName1);
	languagePair1.p()->setCaption(ustr_languageCaption1);
	languagePair2.p()->setLanguage(ustr_langName2);
	languagePair2.p()->setCaption(ustr_languageCaption2);
	languagePair3.p()->setLanguage(ustr_langName3);
	languagePair3.p()->setCaption(ustr_languageCaption3);
	
	spLanguages.p()->IDList_LanguageCaptions.clearIDList();
	spLanguages.p()->IDList_LanguageCaptions.addToIDList(languagePair1);
	spLanguages.p()->IDList_LanguageCaptions.addToIDList(languagePair2);
	spLanguages.p()->IDList_LanguageCaptions.addToIDList(languagePair3);

	langSelector1.p()->ID_USTRING_SelectedLanguage=langPair2.p()->getLanguage().p()->getID();
	
	langStr1.p()->IDList_langPairUStrings.clearIDList();
	langStr1.p()->IDList_langPairUStrings.addToIDList(langPair1);
	langStr1.p()->IDList_langPairUStrings.addToIDList(langPair2);
	langStr1.p()->IDList_langPairUStrings.addToIDList(langPair3);
	langStr1.p()->setLangSelector(langSelector1);
	langStr1.p()->setLanguages(spLanguages);
	
	spFCContainer1.p()->setContainerType(CT_CONTAINER);
	spFCContainer2.p()->setContainerType(CT_CONTAINER);
	spFCContainer3.p()->setContainerType(CT_CONTAINER);

	spFCContainer1.p()->setContainerCaption(L"directory1");
	spFCContainer2.p()->setContainerCaption(L"directory2");
	spFCContainer3.p()->setContainerCaption(L"directory3");

	spFCContainer3.p()->addFileContainerToChilds(ustr_langName1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_langName2.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_langName3.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_langCaption1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_langCaption2.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_langCaption3.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_languageCaption1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_languageCaption2.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(ustr_languageCaption3.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(langPair1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(langPair2.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(langPair3.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(languagePair1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(languagePair2.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(languagePair3.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(langSelector1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(langStr1.p()->getFileContainer());
	spFCContainer3.p()->addFileContainerToChilds(spLanguages.p()->getFileContainer());
  	
	ustr1.p()->wstr=L"abc1";
	ustr2.p()->wstr=L"def2";
	ustr3.p()->wstr=L"ghi3";
	ustr4.p()->wstr=L"jkl4";
	ustr5.p()->wstr=L"mnk5";
	

	spFCContainer2.p()->addFileContainerToChilds(ustr1.p()->getFileContainer());
	spFCContainer2.p()->addFileContainerToChilds(ustr2.p()->getFileContainer());
	spFCContainer2.p()->addFileContainerToChilds(ustr3.p()->getFileContainer());

	spFCContainer1.p()->addFileContainerToChilds(ustr4.p()->getFileContainer());
	spFCContainer1.p()->addFileContainerToChilds(spFCContainer2);
	spFCContainer1.p()->addFileContainerToChilds(ustr5.p()->getFileContainer());
  spFCContainer1.p()->addFileContainerToChilds(spFCContainer3);

	spFCContainer1.p()->setFileName(fname);

	_global.RM.saveAndJoinAll();
	

	//Load
	wstring fname=L"c:/tmp/1/1.txt\0";
	_global.RM.addAndParseAtFile(fname);
	_global.RM.checkAndSetAllObjectsAtIDs();

*/

#ifndef __RESMANAGER_H__
#define __RESMANAGER_H__

#include <stdio.h>
#include <string.h>
#include <typeinfo>
#include <map>

#include "filecontainer.h"
#include "../sptr.h"

#include "simple/ustring.h"
#include "simple/idlist.h"
#include "simple/position.h"
#include "simple/size_xy.h"
#include "language/langpairstring.h"
#include "language/langselector.h"
#include "language/langstring.h"
#include "language/languages.h"
#include "simple/limit.h"
#include "simple/transformation.h"
//MACROS000:#include "#"

#ifdef _DEBUG
//#ifdef _CRTDBG_MAP_ALLOC
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
//#endif 
#endif 

class ResManager{

private:

	map<__int64,SPtr<FileContainer>>            fileContainers;

  map<__int64,SPtr<UString>>                  UStrings;
	map<__int64,SPtr<LangPairString>>           LangPairStrings;
	map<__int64,SPtr<LangSelector>>             LangSelectors;
	map<__int64,SPtr<LangString>>               LangStrings;
	map<__int64,SPtr<Languages>>                Languagess;
	map<__int64,SPtr<Position>>                 Positions;
	map<__int64,SPtr<SizeXY>>                   SizeXYs;
	map<__int64,SPtr<Limit>> Limits;
	map<__int64,SPtr<Transformation>> Transformations;
	//MACROS001:map<__int64,SPtr<#>> #s;

  map<__int64,SPtr<IDList<UString>>>          IDListUStrings;
	map<__int64,SPtr<IDList<LangPairString>>>   IDListLangPairStrings;
	map<__int64,SPtr<IDList<LangSelector>>>     IDListLangSelectors;
	map<__int64,SPtr<IDList<LangString>>>       IDListLangStrings;
	map<__int64,SPtr<IDList<Languages>>>        IDListLanguagess;
	map<__int64,SPtr<IDList<Position>>>         IDListPositions;
  map<__int64,SPtr<IDList<SizeXY>>>           IDListSizeXYs;
	map<__int64,SPtr<IDList<Limit>>> IDListLimits;
	map<__int64,SPtr<IDList<Transformation>>> IDListTransformations;
	//MACROS002:map<__int64,SPtr<IDList<#>>> IDList#s;

	//Разбирает содержимое контейнера (создает и добавляет новый объект(ы), содержащийся в
	//контейнере в список со своим типом)
	int parseAtFileContainer(SPtr<FileContainer> _fc);

	//for create UString,...(Any) at parse Container _inFC
	//Базовым классом T должен быть DataContainerBase
	template <class T> pair<__int64, SPtr<T>> createTAtParse(SPtr<FileContainer> _inFC){
 	  SPtr<T> newVar; 
	  newVar.p()->setFileContainer(_inFC);
	  newVar.p()->parse();
	  return pair<__int64,SPtr<T>>(newVar.p()->getID(),newVar);
  }

	//Для создания списков идентификаторов
	//Базовым классом T должен быть DataContainerBase
	template <class T> pair<__int64, SPtr<IDList<T>>> createListTAtParse(SPtr<FileContainer> _inFC){//, ContainerType _ct){
 	  SPtr<IDList<T>> newVar; 
		//newVar.p()->setIDListType(_ct);
	  newVar.p()->setFileContainer(_inFC);
	  newVar.p()->parse();
	  return pair<__int64,SPtr<IDList<T>>>(newVar.p()->getID(),newVar);
  }

	//Базовым классом T должен быть DataContainerBase
	template <class T> long joinAllInMap(map<__int64,SPtr<T>> *inMap){
	  if(inMap==NULL)return -1;
	  long i=0,res=0;
    map<__int64,SPtr<T>>::iterator iter;
    for(iter=inMap->begin();iter!=inMap->end();iter++){
	    i--;
			if((*iter).second.p()->join()<0){
		    if(res==0)res=i;
	    }
    }
	  return res;
  }

	//Базовым классом T должен быть DataContainerBase
	template <class T> int checkAndSetObjectsAtIDsInMap(map<__int64, SPtr<T>> *_map){
		if(_map==NULL)return -1;
	  map<__int64,SPtr<T>>::iterator iter;
	  for(iter=_map->begin();iter!=_map->end();iter++){
		  (*iter).second.p()->checkAndSetObjectsAtIDs();
	  }
		return 0;
	}

	//Базовым классом T должен быть DataContainerBase
	template <class T> SPtr<T> *getTAtID(map<__int64,SPtr<T>> *mapT, __int64 _id){
		if(mapT==NULL)return NULL;
	  map<__int64,SPtr<T>>::iterator iter;
	  iter=(*mapT).find(_id);
	  if(iter!=(*mapT).end()){
  		return &((*iter).second);
	  }
	  else {
		  return NULL;
	  }
	}

	//Базовым классом T должен быть DataContainerBase
	template <class T> int getObjsToIDList_T(SPtr<T> *(ResManager::*getTAtID)(__int64),map<__int64, SPtr<T>> *mapIDs){
		if(getTAtID==NULL)return -1;
 	  if(mapIDs==NULL)return -2; 
	  map<__int64, SPtr<T>>::iterator iter;
	  SPtr<T> *res;
 	  for(iter=mapIDs->begin();iter!=mapIDs->end();iter++){
		  if((*iter).first!=(*iter).second.p()->getID()){
			  res=(this->*getTAtID)((*iter).first);
			  if(res==NULL)return -1;
        (*iter).second=*res;
		  }
    }
	  return 0;
  }

	//Базовым классом T должен быть DataContainerBase
	template <class T> int addT(map<__int64,SPtr<T>> *mapForInsert, SPtr<T> val, SPtr<FileContainer> *_ParrentFC){
		if(mapForInsert==NULL)return -1;
	  if(_ParrentFC==NULL){
		  (*mapForInsert).insert(pair<__int64,SPtr<T>>(val.p()->getID(),val));
		  return 0;
	  }
	  _ParrentFC->p()->addFileContainerToChilds(val.p()->getFileContainer());
    (*mapForInsert).insert(pair<__int64,SPtr<T>>(val.p()->getID(),val));
	  return 0;
  }

public:

  //Функции поиска объектов по их идентификаторам
	//Для использования в ID_List(использует эти функции при вызове в нем checkAndSetObjectsAtIDs()
	//в map должны быть созданы элементы с идентификаторами.
	//В этих функциях производится дополнение пар, расположенных в map
	//найденными среди ресурсов ResManager элементами с совпадающими
	//идентификаторами
	//IDList автоматически делает эту процедуру для себя при вызове в нем checkAndSetObjectsAtIDs()
	//используя для поиска _global.RM.одна_из_функций_далее
	int getObjsToIDList_UString(map<__int64, SPtr<UString>> *mapIDs);
	int getObjsToIDList_LangPairString(map<__int64, SPtr<LangPairString>> *mapIDs);
	int getObjsToIDList_LangSelector(map<__int64, SPtr<LangSelector>> *mapIDs);
	int getObjsToIDList_LangString(map<__int64, SPtr<LangString>> *mapIDs);
	int getObjsToIDList_Languages(map<__int64, SPtr<Languages>> *mapIDs);
	int getObjsToIDList_Position(map<__int64, SPtr<Position>> *mapIDs);
  int getObjsToIDList_SizeXY(map<__int64, SPtr<SizeXY>> *mapIDs);
	int getObjsToIDList_Limit(map<__int64, SPtr<Limit>> *mapIDs);
	int getObjsToIDList_Transformation(map<__int64, SPtr<Transformation>> *mapIDs);
	//MACROS003:int getObjsToIDList_#(map<__int64, SPtr<#>> *mapIDs);
	
	//Функции поиска объекта по идентификатору
	//Возвращают указатель. Если объект не найден возвращает NULL
	SPtr<UString> *getUStringAtID(__int64 _id);
	SPtr<LangPairString> *getLangPairStringAtID(__int64 _id);
	SPtr<LangSelector> *getLangSelectorAtID(__int64 _id);
	SPtr<LangString> *getLangStringAtID(__int64 _id);
	SPtr<Languages> *getLanguagesAtID(__int64 _id);
	SPtr<Position> *getPositionAtID(__int64 _id);
	SPtr<SizeXY> *getSizeXYAtID(__int64 _id);
	SPtr<Limit> *getLimitAtID(__int64 _id);
	SPtr<Transformation> *getTransformationAtID(__int64 _id);
	//MACROS004:SPtr<#> *get#AtID(__int64 _id);

	//load, add to all resources and parse file with containers
	//Создает объекты на из контейнеров в файле и добавляет объекты в списки ресурсов
  int addAndParseAtFile(wstring &fileName);

	//Создает объект(ы) на основе типа контейнера и добавляет его в один из списков ресурсов
	//_ParrentFC=NULL - добавление контейнера в корень (в fileContainers)
	int addAndParseAtContainer(SPtr<FileContainer> _inFC, SPtr<FileContainer> *_ParrentFC=NULL);

	//Добавляет объект к ресурсам, также может добавить контейнер объекта в
	//выбранный контейнер _ParrentFC
	//если _ParrentFC==NULL то строка просто добавляется в список строк, без добавления контейнера
	//к контейнерам
	int addUString(SPtr<UString> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addLangPairString(SPtr<LangPairString> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addLangSelector(SPtr<LangSelector> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addLangString(SPtr<LangString> val, SPtr<FileContainer> *_ParrentFC=NULL);
	//Добавляет 1 шт объекта Languages
	int addLanguages(SPtr<Languages> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addPosition(SPtr<Position> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addSizeXY(SPtr<SizeXY> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addLimit(SPtr<Limit> val, SPtr<FileContainer> *_ParrentFC=NULL);
	int addTransformation(SPtr<Transformation> val, SPtr<FileContainer> *_ParrentFC=NULL);
	//MACROS005:int add#(SPtr<#> val, SPtr<FileContainer> *_ParrentFC=NULL);

  ResManager();
	~ResManager();

	//Процесс получения ресурсов из файлов разделен на 2 этапа.
	//1. Сначала парсятся все требуемые fileContainer (функции addAndParseAtFile, addAndParseAtContainer)
	//2. Используется функция checkAndSetAllObjectsAtIDs() для установки во всех объектах ResManager взаимных
	//указателей (SPtr) на требуемые объекты по их идентификаторам
	int checkAndSetAllObjectsAtIDs();
	int joinAll();
	int saveAndJoinAll();

	//add parsed elements to all resources at fileContainer
	int parse(SPtr<FileContainer> _inFC);

};


#endif